#include "MeshPassProcessor.h"

#include "Scene.h"
#include "SceneRendering.h"

namespace Alice
{
    void FMeshDrawShaderBindings::Initialize(const FMeshProcessorShaders& Shaders)
    {
        ShaderLayouts.reserve(2);
        ShaderFrequencyBits |= (1 << SF_Vertex);
        ShaderFrequencyBits |= (1 << SF_Pixel);
    }
    void FGraphicsMinimalPipelineStateInitializer::SetupBoundShaderState(CGIVertexDeclaration* inVertexDeclaration, const FMeshProcessorShaders& Shaders)
    {
        BoundShaderState=FMinimalBoundShaderStateInput();
        BoundShaderState.mCGIVertexDeclaration=inVertexDeclaration;
        BoundShaderState.mCachedCGIVertexShader=Shaders.mCGIVertexShader;
        BoundShaderState.mCachedCGIPixelShader=Shaders.mCGIPixelShader;
    }
    
    /**
     * Get a ref counted persistent pipeline id, which needs to manually released.
     */
    FGraphicsMinimalPipelineStateId FGraphicsMinimalPipelineStateId::GetPersistentId(const FGraphicsMinimalPipelineStateInitializer& InPipelineState)
    {
        FGraphicsMinimalPipelineStateId Ret;
        Ret.bValid = 1;
        Ret.bComesFromLocalPipelineStateSet = 1;
        Ret.SetElementIndex = 0;
        return Ret;
    }
    const FMeshDrawCommandSortKey FMeshDrawCommandSortKey::Default = { {0} };
    PassProcessorCreateFunction FPassProcessorManager::mJumpTable[(uint32)EShadingPath::Num][EMeshPass::Num]={
        {
            nullptr,//DepthPass,
            nullptr,//SecondStageDepthPass,
            nullptr,//BasePass,
            nullptr,//AnisotropyPass,
            nullptr,//SkyPass,
            nullptr,//SingleLayerWaterPass,
            nullptr,//SingleLayerWaterDepthPrepass,
            nullptr,//CSMShadowDepth,
            nullptr,//VSMShadowDepth,
            nullptr,//OnePassPointLightShadowDepth,
            nullptr,//Distortion,
            nullptr,//Velocity,
            nullptr,//TranslucentVelocity,
            nullptr,//TranslucencyStandard,
            nullptr,//TranslucencyStandardModulate,
            nullptr,//TranslucencyAfterDOF,
            nullptr,//TranslucencyAfterDOFModulate,
            nullptr,//TranslucencyAfterMotionBlur,
            nullptr,//TranslucencyHoldout, /** A standalone pass to render all translucency for holdout, inferring the background visibility*/
            nullptr,//TranslucencyAll, /** Drawing all translucency, regardless of separate or standard.  Used when drawing translucency outside of the main renderer, eg FRendererModule::DrawTile. */
            nullptr,//LightmapDensity,
            nullptr,//DebugViewMode, /** Any of EDebugViewShaderMode */
            nullptr,//CustomDepth,
            nullptr,//MobileBasePassCSM,  /** Mobile base pass with CSM shading enabled */
            nullptr,//VirtualTexture,
            nullptr,//LumenCardCapture,
            nullptr,//LumenCardNanite,
            nullptr,//LumenTranslucencyRadianceCacheMark,
            nullptr,//LumenFrontLayerTranslucencyGBuffer,
            nullptr,//DitheredLODFadingOutMaskPass, /** A mini depth pass used to mark pixels with dithered LOD fading out. Currently only used by ray tracing shadows. */
            nullptr,//NaniteMeshPass,
            nullptr,//MeshDecal_DBuffer,
            nullptr,//MeshDecal_SceneColorAndGBuffer,
            nullptr,//MeshDecal_SceneColorAndGBufferNoNormal,
            nullptr,//MeshDecal_SceneColor,
            nullptr,//MeshDecal_AmbientOcclusion,
            nullptr,//WaterInfoTextureDepthPass,
            nullptr,//WaterInfoTexturePass,
        },
        {
            nullptr,//DepthPass,
            nullptr,//SecondStageDepthPass,
            nullptr,//BasePass,
            nullptr,//AnisotropyPass,
            nullptr,//SkyPass,
            nullptr,//SingleLayerWaterPass,
            nullptr,//SingleLayerWaterDepthPrepass,
            nullptr,//CSMShadowDepth,
            nullptr,//VSMShadowDepth,
            nullptr,//OnePassPointLightShadowDepth,
            nullptr,//Distortion,
            nullptr,//Velocity,
            nullptr,//TranslucentVelocity,
            nullptr,//TranslucencyStandard,
            nullptr,//TranslucencyStandardModulate,
            nullptr,//TranslucencyAfterDOF,
            nullptr,//TranslucencyAfterDOFModulate,
            nullptr,//TranslucencyAfterMotionBlur,
            nullptr,//TranslucencyHoldout, /** A standalone pass to render all translucency for holdout, inferring the background visibility*/
            nullptr,//TranslucencyAll, /** Drawing all translucency, regardless of separate or standard.  Used when drawing translucency outside of the main renderer, eg FRendererModule::DrawTile. */
            nullptr,//LightmapDensity,
            nullptr,//DebugViewMode, /** Any of EDebugViewShaderMode */
            nullptr,//CustomDepth,
            nullptr,//MobileBasePassCSM,  /** Mobile base pass with CSM shading enabled */
            nullptr,//VirtualTexture,
            nullptr,//LumenCardCapture,
            nullptr,//LumenCardNanite,
            nullptr,//LumenTranslucencyRadianceCacheMark,
            nullptr,//LumenFrontLayerTranslucencyGBuffer,
            nullptr,//DitheredLODFadingOutMaskPass, /** A mini depth pass used to mark pixels with dithered LOD fading out. Currently only used by ray tracing shadows. */
            nullptr,//NaniteMeshPass,
            nullptr,//MeshDecal_DBuffer,
            nullptr,//MeshDecal_SceneColorAndGBuffer,
            nullptr,//MeshDecal_SceneColorAndGBufferNoNormal,
            nullptr,//MeshDecal_SceneColor,
            nullptr,//MeshDecal_AmbientOcclusion,
            nullptr,//WaterInfoTextureDepthPass,
            nullptr,//WaterInfoTexturePass,
        }
    };
    FMeshPassProcessor::FMeshPassProcessor(EMeshPass::Type InMeshPassType, const Scene* InScene, const SceneView* InViewIfDynamicMeshCommand, FMeshPassDrawListContext* InDrawListContext)
    : IPSOCollector(0)
    , MeshPassType(InMeshPassType)
    , mScene(InScene)
    , ViewIfDynamicMeshCommand(InViewIfDynamicMeshCommand)
    , DrawListContext(InDrawListContext)
    {
        
    }
    void FMeshPassProcessor::AddMeshBatch(const FMeshBatch& MeshBatch, uint64 BatchElementMask, const PrimitiveSceneProxy* PrimitiveSceneProxy, int32 StaticMeshId)
    {
        
    }
    FMeshDrawCommand& FCachedPassMeshDrawListContext::AddCommand(FMeshDrawCommand& Initializer, uint32 NumElements)
    {
        return Initializer;
    }
    void FMeshDrawCommand::SetDrawParametersAndFinalize(const FMeshBatch& MeshBatch, int32 BatchElementIndex, FGraphicsMinimalPipelineStateId PipelineId, const FMeshProcessorShaders* ShadersForDebugging)
    {
	    const FMeshBatchElement& meshBatchElement = MeshBatch.mMeshBatchElements[BatchElementIndex];
        
        FirstIndex = meshBatchElement.FirstIndex;
        NumPrimitives = meshBatchElement.NumPrimitives;
        NumInstances = meshBatchElement.NumInstances;
        VertexParams.BaseVertexIndex = meshBatchElement.BaseVertexIndex;
        VertexParams.NumVertices = meshBatchElement.MaxVertexIndex - meshBatchElement.MinVertexIndex + 1;

        Finalize(PipelineId,ShadersForDebugging);
    }
    void FCachedPassMeshDrawListContext::FinalizeCommandCommon(
        const FMeshBatch& inMeshBatch,
        int32 inBatchElementIndex,
        ERasterizerFillMode inMeshFillMode,
        ERasterizerCullMode inMeshCullMode,
        FMeshDrawCommandSortKey inSortKey,
        EFVisibleMeshDrawCommandFlags inFlags,
        const FGraphicsMinimalPipelineStateInitializer& inPipelineState,
        const FMeshProcessorShaders* inShadersForDebugging,
        FMeshDrawCommand& inMeshDrawCommand)
    {
        FGraphicsMinimalPipelineStateId pipelineStateID=FGraphicsMinimalPipelineStateId::GetPersistentId(inPipelineState);
        inMeshDrawCommand.SetDrawParametersAndFinalize(inMeshBatch,inBatchElementIndex,pipelineStateID,inShadersForDebugging);
    }

    FCachedMeshDrawCommandInfo FCachedPassMeshDrawListContext::GetCommandInfoAndReset()
    {
        FCachedMeshDrawCommandInfo ret=CommandInfo;
        CommandInfo.CommandIndex=-1;
        CommandInfo.StateBucketId=-1;
        return ret;
    }

    void FCachedPassMeshDrawListContextImmediate::FinalizeCommand(
        const FMeshBatch& MeshBatch,
        int32 BatchElementIndex,
        const FMeshDrawCommandPrimitiveIdInfo& IdInfo,
        ERasterizerFillMode MeshFillMode,
        ERasterizerCullMode MeshCullMode,
        FMeshDrawCommandSortKey SortKey,
        EFVisibleMeshDrawCommandFlags Flags,
        const FGraphicsMinimalPipelineStateInitializer& PipelineState,
        const FMeshProcessorShaders* ShadersForDebugging,
        FMeshDrawCommand& MeshDrawCommand)
    {
        if(bUseGPUScene)
        {
            
        } else {
            FCachedPassMeshDrawList& cachedPassMeshDrawList=mScene.mCachedPassMeshDrawList[CurrMeshPass];
            CommandInfo.CommandIndex=cachedPassMeshDrawList.MeshDrawCommands.size();
            cachedPassMeshDrawList.MeshDrawCommands.push_back(MeshDrawCommand);
        }
    }
    FMeshBatchAndRelevance::FMeshBatchAndRelevance(const FMeshBatch& inMesh, const PrimitiveSceneProxy* inPrimitiveSceneProxy)
        :mMesh(&inMesh),mPrimitiveSceneProxy(inPrimitiveSceneProxy)
    {
        
    }
    void FParallelMeshDrawCommandPass::DispatchPassSetup(
        Scene* inScene,
        const ViewInfo& inViewInfo,
        EMeshPass::Type PassType,
        FMeshPassProcessor* MeshPassProcessor,
        const std::vector<FMeshBatchAndRelevance>& DynamicMeshElements,
        const std::vector<FMeshPassMask>* DynamicMeshElementsPassRelevance,
        int32 NumDynamicMeshElements,
        std::vector<const FStaticMeshBatch*>& InOutDynamicMeshCommandBuildRequests,
        std::vector<EMeshDrawCommandCullingPayloadFlags> InOutDynamicMeshCommandBuildFlags,
        int32 NumDynamicMeshCommandBuildRequestElements,
        std::vector<FVisibleMeshDrawCommand*>& InOutMeshDrawCommands,
        FMeshPassProcessor* MobileBasePassCSMMeshPassProcessor,
        std::vector<FVisibleMeshDrawCommand*>* InOutMobileBasePassCSMMeshDrawCommands)
    {
        
    }
    void FParallelMeshDrawCommandPass::Draw(CGICommandList& RHICmdList) const
    {
        FMeshDrawCommandSceneArgs SceneArgs;
        SubmitMeshDrawCommandsRange(
            mMeshDrawCommandPassSetupTaskContext.mMeshDrawCommands,
            mMeshDrawCommandPassSetupTaskContext.mMinimalPipelineStatePassSet,
            SceneArgs,
            0,
            mMeshDrawCommandPassSetupTaskContext.bDynamicInstancing,
            0,
            mMeshDrawCommandPassSetupTaskContext.mMeshDrawCommands.size(),
            mMeshDrawCommandPassSetupTaskContext.InstanceFactor,
            RHICmdList);
    }
    void SubmitMeshDrawCommandsRange(
        const std::vector<FVisibleMeshDrawCommand*>& VisibleMeshDrawCommands,
        const std::unordered_set<FGraphicsMinimalPipelineStateInitializer*>& GraphicsMinimalPipelineStateSet,
        const FMeshDrawCommandSceneArgs& inSceneArgs,
        uint32 PrimitiveIdBufferStride,
        bool bDynamicInstancing,
        int32 StartIndex,
        int32 NumMeshDrawCommands,
        uint32 InstanceFactor,
        CGICommandList& RHICmdList)
    {
        FMeshDrawCommandStateCache StateCache;
        FMeshDrawCommandSceneArgs LocalSceneArgs = inSceneArgs;
        for (int32 DrawCommandIndex = StartIndex; DrawCommandIndex < StartIndex + NumMeshDrawCommands; DrawCommandIndex++)
        {
		    const FVisibleMeshDrawCommand* VisibleMeshDrawCommand = VisibleMeshDrawCommands[DrawCommandIndex];
		    FMeshDrawCommand::SubmitDraw(*(VisibleMeshDrawCommand->mMeshDrawCommand), GraphicsMinimalPipelineStateSet, LocalSceneArgs, InstanceFactor, RHICmdList, StateCache);
        }
    }
    bool FMeshDrawCommand::SubmitDrawBegin(
        const FMeshDrawCommand& MeshDrawCommand,
        const std::unordered_set<FGraphicsMinimalPipelineStateInitializer*>& GraphicsMinimalPipelineStateSet,
        const FMeshDrawCommandSceneArgs& SceneArgs,
        uint32 InstanceFactor,
        CGICommandList& RHICmdList,
        FMeshDrawCommandStateCache& StateCache,
        bool bAllowSkipDrawCommand)
    {
        return true;
    }
    void FMeshDrawCommand::SubmitDrawEnd(
        const FMeshDrawCommand& inMeshDrawCommand,
        const FMeshDrawCommandSceneArgs& SceneArgs,
        uint32 inInstanceFactor,
        CGICommandList& RHICmdList)
    {
        RHICmdList.DrawPrimitive(
            inMeshDrawCommand.VertexParams.BaseVertexIndex+inMeshDrawCommand.FirstIndex,
            inMeshDrawCommand.NumPrimitives,
            inMeshDrawCommand.NumInstances*inInstanceFactor);
    }
    bool FMeshDrawCommand::SubmitDraw(
        const FMeshDrawCommand& MeshDrawCommand,
        const std::unordered_set<FGraphicsMinimalPipelineStateInitializer*>& GraphicsMinimalPipelineStateSet,
        const FMeshDrawCommandSceneArgs& SceneArgs,
        uint32 InstanceFactor,
        CGICommandList& inCGICommandList,
        FMeshDrawCommandStateCache& StateCache)
    {
        bool bAllowSkipDrawCommand = true;
        if(SubmitDrawBegin(MeshDrawCommand, GraphicsMinimalPipelineStateSet, SceneArgs, InstanceFactor, inCGICommandList, StateCache, bAllowSkipDrawCommand))
        {
            SubmitDrawEnd(MeshDrawCommand, SceneArgs, InstanceFactor, inCGICommandList);
            return true;
        }
        return false;
    }

}
